package com.swordliu.facebook.config.shiro;

import com.swordliu.facebook.Utils.SecureUtils;
import com.swordliu.facebook.pojo.entity.User;
import com.swordliu.facebook.service.PermissionService;
import com.swordliu.facebook.service.RoleService;
import com.swordliu.facebook.service.UserService;
import org.apache.shiro.authc.*;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;

import java.io.UnsupportedEncodingException;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * ShiroRealm
 * Created by SwordLiu on 2017-07-05.
 */
public class ShiroRealm extends AuthorizingRealm {

    @Autowired
    private UserService userService;
    @Autowired
    private RoleService roleService;
    @Autowired
    private PermissionService permissionService;

    private Cache<String, AtomicInteger> passwordRetryCache;

    /**
     * 配置密码重试次数记录缓存
     * @param ehCacheManager
     */
    @Autowired
    private void retryLimitCredentialsMatcher(CacheManager ehCacheManager) {
        passwordRetryCache = ehCacheManager.getCache("passwordRetryCache");
    }

    /**
     * 授权
     * @param principals
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        String username = (String) principals.getPrimaryPrincipal();
        User user = null;
        try {
            user = userService.getUser(username);
        } catch (Exception e) {
            e.printStackTrace();
        }
//        User user = (User) principals.getPrimaryPrincipal();

        Set<String> roles = roleService.getRoleKeys(user.getId());
        Set<String> permissions = permissionService.getPermissionKeys(user.getId());

        authorizationInfo.setRoles(roles);
        authorizationInfo.setStringPermissions(permissions);
        return authorizationInfo;
    }

    /**
     * 登录认证
     * @param authcToken
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {
//        UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
        String username = (String) authcToken.getPrincipal();
        User user = null;
        try {
            user = userService.getUser(username);
        } catch (Exception e) {
            e.printStackTrace();
        }
        //账号不存在
        if (user == null) {
            throw new UnknownAccountException();
        }
        //账号锁定
//        if (Boolean.TRUE.equals(user.getLock())) {
//            throw new LockedAccountException();
//        }

        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
                user.getUsername(),
                user.getPassword(),
                ByteSource.Util.bytes(user.getSalt()),
                getName());

        return authenticationInfo;
    }

    /**
     * 设置shiro默认凭证匹配方法，我完全重写了shiro的凭证匹配，并植入登录次数限制：简单除暴
     * 不再调用系统的{@code CredentialsMatcher doCredentialsMatch}方法
     * 如果想要更好的结构和安全性，将自定义的matcher写成一个独立的如{HashedCredentialsMatcher}一样的类，继承{simpleCredentialsMatcher}
     * 这样，就可以将次数限制功能提出来，单独实现，见{CredentialsMatcher}
     * @param credentialsMatcher 该属性不再被使用
     */
    @Override
    public void setCredentialsMatcher(CredentialsMatcher credentialsMatcher) {
        CredentialsMatcher matcher = (token, info) -> {
            UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
            String username = usernamePasswordToken.getUsername();
            String password = String.valueOf(usernamePasswordToken.getPassword());
            User user = new User(username, password);
            if (info instanceof SaltedAuthenticationInfo) {
                ByteSource salt = ((SaltedAuthenticationInfo) info).getCredentialsSalt();
                try {
                    user.setSalt(new String(salt.getBytes(), "UTF-8"));
                } catch (UnsupportedEncodingException e) {
                }
            }
            user = SecureUtils.encryptionPassword(user);


            //登录次数限制
            //获取已经登录的次数
            AtomicInteger retryCount = passwordRetryCache.get(username);
            //如果是第一次登录，则创建一个新的AtomicInteger(原子操作Integer)
            if (null == retryCount) {
                retryCount = new AtomicInteger(0);
                passwordRetryCache.put(username, retryCount);
            }
            //如果登录尝试次数大于5次，则禁用一定时间
            if (retryCount.incrementAndGet() > 3) {
                throw new RuntimeException("登录尝试次数超3次了");
            }

            //执行登录凭证匹配，如果匹配成功，则移除登录次数的缓存
            boolean match = user.getPassword().equals(info.getCredentials().toString());
            if (match) {
                passwordRetryCache.remove(username);
            }


            return match;
        };
        super.setCredentialsMatcher(matcher);
    }
}
